use charming::{
    component::{Axis, Title},
    datatype::{CompositeValue, DataFrame, DataPoint, Dataset},
    element::{
        Label, LabelPosition, LineStyle, LineStyleType, MarkLine, MarkLineData, MarkLineVariant,
        Symbol,
    },
};
use chrono::{DateTime, Datelike, Days, Months, NaiveDate, Utc};
use numfmt::*;
use serde_json::Value;
use shq_common::prelude::{json_value_to_string, report::TimeType};
use shq_report::{get_rept_config, Report};
use std::collections::BTreeMap;

use crate::chart_series::forecast::ForecastService;
pub struct SeriesDataList {
    //时间类型
    time_type: TimeType,
    datas: Vec<SeriesData>,
    show_period: u32,
    forecast_period: Option<u32>,
    max_x_val: String,
}
impl SeriesDataList {
    pub fn new(time_type: TimeType, show_period: u32, forecast_period: Option<u32>) -> Self {
        Self {
            time_type,
            datas: Vec::new(),
            show_period,
            forecast_period,
            max_x_val: "".to_owned(),
        }
    }
    pub fn get_first_selected_index(&self,sname:Option<&str>) -> usize {
        self.datas.iter().position(|sd| {
            if let Some(sname)=sname{
                sd.name.as_str()==sname
            }else{
                false
            }
        }).unwrap_or(0)
    }
    pub fn from_rept(
        rept_conf: &'static Report,
        row_datas: Vec<Vec<Value>>,
    ) -> anyhow::Result<Option<Self>> {
        if let Some(ss) = &rept_conf.series_source {
            let name_idx = rept_conf
                .columns
                .iter()
                .position(|c| c.name.as_deref() == Some(ss.name_column.as_str()))
                .expect("series_source.name_column在列中未找到");
            let y_idx = rept_conf
                .columns
                .iter()
                .position(|c| c.name.as_deref() == Some(ss.y_column.as_str()))
                .expect("series_source.y_column在列中未找到");
            let x_idx = rept_conf
                .columns
                .iter()
                .position(|c| c.name.as_deref() == Some(ss.x_column.as_str()))
                .expect("series_source.x_column在列中未找到");
            let mut sdl = SeriesDataList::new(ss.time_type, ss.show_period, ss.forecast_period);
            for row in row_datas.into_iter() {
                let name_str = row[name_idx].as_str().unwrap_or("").to_string();
                let idx_opt = sdl
                    .datas
                    .iter()
                    .position(|sd| sd.name.as_str() == name_str.as_str());
                let sd = if let Some(idx) = idx_opt {
                    &mut sdl.datas[idx]
                } else {
                    sdl.datas
                        .push(SeriesData::new(name_str.clone(), ss.time_type));
                    sdl.datas.last_mut().unwrap()
                };
                let x_val = json_value_to_string(&row[x_idx]);
                let y_val = to_f64(&row[y_idx])?;
                if x_val > sdl.max_x_val {
                    sdl.max_x_val = x_val.to_string();
                }
                sd.add_points(x_val, y_val)?;
            }
            Ok(Some(sdl))
        } else {
            Ok(None)
        }
    }
    pub fn get_x_vals(&self) -> anyhow::Result<Vec<String>> {
        if self.time_type == TimeType::None {
            self.datas
                .first()
                .map(|sd| sd.points.keys().cloned().collect())
                .ok_or_else(|| anyhow::anyhow!("没有系列数据"))
        } else {
            let now = str_to_timestamp(self.time_type, &self.max_x_val)?;
            let dvals = get_x_vals(
                self.time_type,
                now.date_naive(),
                self.show_period,
                self.forecast_period,
            )?;
            let x_vals: Vec<String> = dvals
                .into_iter()
                .map(|dt| date_to_value(dt, self.time_type))
                .collect();
            Ok(x_vals)
        }
    }
    pub fn forecast(&mut self) -> anyhow::Result<()> {
        let fp = self.forecast_period.unwrap_or(0);
        if fp == 0 {
            return Ok(());
        }
        for sd in self.datas.iter_mut() {
            sd.forecast(fp as usize)?;
        }
        Ok(())
    }
    pub fn into_inner(self) -> Vec<SeriesData> {
        self.datas
    }
}
pub struct SeriesData {
    //系列名称
    name: String,
    //数据点
    points: BTreeMap<String, f64>,
    //预测值
    forecast_vals: Vec<f64>,
    //时间类型
    time_type: TimeType,
}
impl SeriesData {
    pub fn new(name: String, time_type: TimeType) -> Self {
        Self {
            name,
            points: BTreeMap::new(),
            forecast_vals: Vec::new(),
            time_type,
        }
    }
    pub fn add_points(&mut self, x_val: String, y_val: f64) -> anyhow::Result<()> {
        self.points.insert(x_val, y_val);
        Ok(())
    }
    // 时间序列预测
    pub fn forecast(&mut self, periods: usize) -> anyhow::Result<()> {
        if periods == 0 {
            return Ok(());
        }
        let mut times: Vec<DateTime<Utc>> = Vec::new();
        let mut vals: Vec<f64> = Vec::new();
        for (k, v) in self.points.iter() {
            let tv = str_to_timestamp(self.time_type, k)?;
            times.push(tv);
            vals.push(*v);
        }
        let fs = ForecastService::new(times, vals, &self.name, periods).or_else(|err| {
            anyhow::bail!("系列:{} 进行预测时发生错误:{}", self.name, err);
        })?;
        let (forecast_vals, _model_used) = fs.auto_select()?;
        self.forecast_vals = forecast_vals;
        Ok(())
    }
    pub fn get_y_vals(&self, x_vals: &[String]) -> Vec<Value> {
        let mut y_vals: Vec<Value> = Vec::new();
        for x in x_vals.iter() {
            if let Some(v) = self.points.get(x) {
                y_vals.push(Value::from(*v));
            } else {
                y_vals.push(Value::Null);
            }
        }
        for v in self.forecast_vals.iter() {
            y_vals.push(Value::from(*v));
        }
        y_vals
    }
}
pub fn get_chart_with_dataset(
    title: Option<String>,
    rept_name: &str,
    row_datas: Vec<Vec<Value>>,
) -> anyhow::Result<charming::Chart> {
    let rept_conf =
        get_rept_config(rept_name).ok_or_else(|| anyhow::anyhow!("报表{}配置不存在", rept_name))?;
    let mut chart = rept_conf
        .chart
        .clone()
        .ok_or_else(|| anyhow::anyhow!("没有定义chart"))?;
    if let Some(title) = title {
        let t = Title::new().text(title);
        chart = chart.title(t);
    }
    if rept_conf.series_source.is_some() {
        let mut sdl = SeriesDataList::from_rept(rept_conf, row_datas)?.unwrap();
        sdl.forecast()?;
        let s = chart.move_first_series();
        let x_vals = sdl.get_x_vals()?;
        let mut x_axis = chart.move_first_x_axis().unwrap_or(Axis::new());
        x_axis = x_axis.data(x_vals.clone());
        chart = chart.x_axis(x_axis);
        let fp = rept_conf
            .series_source
            .as_ref()
            .unwrap()
            .forecast_period
            .unwrap_or(0);
        let ss = rept_conf.series_source.as_ref().unwrap();
        let show_period = ss.show_period;
        if let Some(s) = s {
            let idx = sdl.get_first_selected_index(ss.first_name.as_deref());
            for (i, sd) in sdl.into_inner().into_iter().enumerate() {
                let y_vals = sd.get_y_vals(&x_vals[0..x_vals.len() - fp as usize]);
                let mut s2 = s
                    .clone()
                    .set_name(sd.name)
                    .set_data_frame(to_data_frame(y_vals, ss.precision));
                if i == idx && fp > 0 {
                    let split = show_period - fp - 1;
                    let label = Label::new()
                        .position(LabelPosition::Bottom)
                        .show(true)
                        .color("#e63946")
                        .font_weight("bold")
                        .formatter("预测开始");
                    let ml_data = MarkLineData::new()
                        .name("预测开始")
                        .label(label)
                        .x_axis(split as i32);
                    let mark_line = MarkLine::new()
                        .symbol(vec![Symbol::None, Symbol::None])
                        .data(vec![MarkLineVariant::Simple(ml_data)])
                        .line_style(
                            LineStyle::new()
                                .type_(LineStyleType::Dashed)
                                .width(2)
                                .color("#e63946"),
                        );
                    s2 = s2.set_mark_line(mark_line);
                }
                chart.add_series(s2);
            }
        }
        Ok(chart)
    } else {
        let dims: Vec<Value> = rept_conf
            .columns
            .iter()
            .filter_map(|c| c.title.as_ref().map(|t| Value::from(t.clone())))
            .collect::<Vec<_>>();
        let mut datas: Vec<Vec<Value>> = Vec::with_capacity(row_datas.len() + 1);
        datas.push(dims);
        datas.extend(row_datas);
        let dataset = to_dataset(datas);
        Ok(chart.dataset(dataset))
    }
}

fn to_dataset(row_datas: Vec<Vec<Value>>) -> Dataset {
    let mut datas = Vec::new();
    for row in row_datas {
        let composite_row = row
            .into_iter()
            .map(|v| CompositeValue::from(v))
            .collect::<Vec<CompositeValue>>();
        datas.push(composite_row);
    }
    Dataset::new().source(datas)
}
fn to_data_frame(row_data: Vec<Value>, precision: u8) -> DataFrame {
    let mut datas: Vec<DataPoint> = Vec::new();
    for v in row_data {
        if let Value::Number(nv) = v {
            if nv.is_f64() {
                let fval = nv.as_f64().unwrap();
                let mut f = Formatter::new();
                f = f.precision(Precision::Decimals(precision));
                let sval = f.fmt2(fval);
                datas.push(CompositeValue::from(sval).into());
            } else {
                datas.push(CompositeValue::from(Value::Number(nv)).into());
            }
        } else {
            datas.push(CompositeValue::from(v).into());
        }
    }
    datas
}
fn str_to_timestamp(time_type: TimeType, s: &str) -> anyhow::Result<DateTime<Utc>> {
    let date_str=match time_type {
        TimeType::Day => s.to_owned(),
        TimeType::Month => format!("{}01", s),
        TimeType::Quarter => {
            let year: i32 = s[0..4]
                .parse()
                .map_err(|_e| anyhow::anyhow!("无效的季度: {}", s))?;
            let quarter: u32 = s[4..5].parse().map_err(|_e| {
                anyhow::anyhow!("无效的季度: {}", s)
            })?;
            let month = match quarter {
                1 => 3,
                2 => 6,
                3 => 9,
                4 => 12,
                _ => return Err(anyhow::anyhow!("Invalid quarter value: {}", quarter)),
            };
            format!("{:04}{:02}01", year, month)
        }
        TimeType::Year => format!("{}1231", s),
        TimeType::None => {
            anyhow::bail!("时间类型不能为None");
        }
    };
    let mut date=NaiveDate::parse_from_str(&date_str, "%Y%m%d")?;
    if time_type==TimeType::Month || time_type==TimeType::Quarter {
        date = date
            .checked_add_months(Months::new(1))
            .ok_or_else(|| anyhow::anyhow!("Date overflow when calculating month end"))?
            .checked_sub_days(Days::new(1))
            .ok_or_else(|| {
                anyhow::anyhow!("Date overflow when calculating month end")
            })?;
    }
    Ok(date.and_hms_opt(0, 0, 0).unwrap().and_utc())
}
fn to_timestamp(time_type: TimeType, value: &Value) -> anyhow::Result<DateTime<Utc>> {
    match value {
        Value::Number(num) => {
            if num.is_i64() {
                let ts = num.as_i64().unwrap();
                let (year, month, day) = match time_type {
                    TimeType::Day => {
                        let year = (ts / 10000) as i32;
                        let month = ((ts % 10000) / 100) as u32;
                        let day = (ts % 100) as u32;
                        (year, month, day)
                    }
                    TimeType::Month => {
                        let year = (ts / 100) as i32;
                        let month = (ts % 100) as u32;
                        (year, month, 1)
                    }
                    TimeType::Quarter => {
                        let year = (ts / 10) as i32;
                        let quarter = (ts % 10) as u32;
                        let month = match quarter {
                            1 => 3,
                            2 => 6,
                            3 => 9,
                            4 => 12,
                            _ => return Err(anyhow::anyhow!("Invalid quarter value: {}", quarter)),
                        };
                        (year, month, 1)
                    }
                    TimeType::Year => {
                        let year = ts as i32;
                        (year, 12, 31)
                    }
                    TimeType::None => {
                        return Err(anyhow::anyhow!("时间类型不能为None"));
                    }
                };
                let mut date = NaiveDate::from_ymd_opt(year, month, day).ok_or_else(|| {
                    anyhow::anyhow!(
                        "Invalid date components: year={}, month={}, day={}",
                        year,
                        month,
                        day
                    )
                })?;
                if time_type == TimeType::Month || time_type == TimeType::Quarter {
                    date = date
                        .checked_add_months(Months::new(1))
                        .ok_or_else(|| anyhow::anyhow!("Date overflow when calculating month end"))?
                        .checked_sub_days(Days::new(1))
                        .ok_or_else(|| {
                            anyhow::anyhow!("Date overflow when calculating month end")
                        })?;
                }
                let naive = date
                    .and_hms_opt(0, 0, 0)
                    .ok_or_else(|| anyhow::anyhow!("Invalid time components for date: {}", date))?;
                let datetime: DateTime<Utc> = DateTime::from_naive_utc_and_offset(naive, Utc);
                Ok(datetime)
            } else {
                return Err(anyhow::anyhow!(
                    "Invalid timestamp value: expected i64 number"
                ));
            }
        }
        Value::String(s) => {
            let timestamp = DateTime::parse_from_rfc3339(&s)
                .map(|dt| dt.with_timezone(&Utc))
                .map_err(|e| anyhow::anyhow!("Failed to parse timestamp: {}", e))?;
            Ok(timestamp)
        }
        _ => {
            return Err(anyhow::anyhow!(
                "Invalid timestamp value: expected string or number"
            ));
        }
    }
}
fn to_f64(val: &Value) -> anyhow::Result<f64> {
    match val {
        Value::Number(num) => {
            if let Some(f) = num.as_f64() {
                Ok(f)
            } else {
                return Err(anyhow::anyhow!("Invalid value: expected f64 number"));
            }
        }
        Value::String(s) => {
            let f: f64 = s
                .parse()
                .map_err(|e| anyhow::anyhow!("Failed to parse value string to f64: {}", e))?;
            Ok(f)
        }
        _ => return Err(anyhow::anyhow!("Invalid value: expected string or number")),
    }
}
fn get_x_vals(
    time_type: TimeType,
    now: NaiveDate,
    show_period: u32,
    forecast_period: Option<u32>,
) -> anyhow::Result<Vec<DateTime<Utc>>> {
    let mut forecast_times: Vec<DateTime<Utc>> = Vec::new();
    let remain_periods = show_period - forecast_period.unwrap_or(0);
    let start_time = get_pre_day(time_type, now, remain_periods)?;
    for i in 1..=show_period as usize {
        let date = get_next_day(time_type, start_time, i as u32)?;
        forecast_times.push(date.and_hms_opt(0, 0, 0).unwrap().and_utc());
    }
    Ok(forecast_times)
}
fn date_to_value(dt: DateTime<Utc>, time_type: TimeType) -> String {
    match time_type {
        TimeType::Day => {
            let y = dt.year();
            let m = dt.month();
            let d = dt.day();
            format!("{:04}{:02}{:02}", y, m, d)
        }
        TimeType::Month => {
            let y = dt.year();
            let m = dt.month();
            format!("{:04}{:02}", y, m)
        }
        TimeType::Quarter => {
            let y = dt.year();
            let quarter = (dt.month() - 1) / 3 + 1;
            format!("{:04}{}", y, quarter)
        }
        TimeType::Year => {
            let y = dt.year();
            format!("{:04}", y)
        }
        TimeType::None => "".to_owned(),
    }
}

fn get_pre_day(time_type: TimeType, now: NaiveDate, steps: u32) -> anyhow::Result<NaiveDate> {
    let start_time = match time_type {
        TimeType::Day => now
            .checked_sub_signed(chrono::Duration::days(steps as i64))
            .ok_or_else(|| anyhow::anyhow!("Date overflow when subtracting days"))?,
        TimeType::Month => now
            .with_day(1)
            .unwrap()
            .checked_sub_months(Months::new(steps - 1))
            .unwrap()
            .checked_sub_days(Days::new(1))
            .ok_or_else(|| anyhow::anyhow!("Date overflow when subtracting months"))?,
        TimeType::Quarter => {
            let m = now.month();
            let m2 = match m {
                1 | 2 | 3 => 3,
                4 | 5 | 6 => 6,
                7 | 8 | 9 => 9,
                10 | 11 | 12 => 12,
                _ => 3,
            };
            let qdate = now
                .with_month(m2)
                .unwrap()
                .with_day(1)
                .unwrap()
                .checked_add_months(Months::new(1))
                .unwrap()
                .checked_sub_days(Days::new(1))
                .unwrap();
            qdate
                .checked_sub_months(Months::new(steps * 3))
                .ok_or_else(|| anyhow::anyhow!("Date overflow when subtracting quarters"))?
        }
        TimeType::Year => now
            .with_month(1)
            .unwrap()
            .with_day(1)
            .unwrap()
            .checked_sub_signed(chrono::Duration::days(1))
            .unwrap()
            .checked_sub_months(Months::new(steps * 12))
            .ok_or_else(|| anyhow::anyhow!("Date overflow when subtracting years"))?,
        TimeType::None => {
            anyhow::bail!("时间类型不能为None");
        }
    };
    Ok(start_time)
}

fn get_next_day(time_type: TimeType, now: NaiveDate, steps: u32) -> anyhow::Result<NaiveDate> {
    let start_time = match time_type {
        TimeType::Day => now
            .with_day(1)
            .unwrap()
            .checked_add_days(Days::new(steps as u64))
            .ok_or_else(|| anyhow::anyhow!("Date overflow when adding days"))?,
        TimeType::Month => now
            .with_day(1)
            .unwrap()
            .checked_add_months(Months::new(steps + 1))
            .unwrap()
            .checked_sub_days(Days::new(1))
            .ok_or_else(|| anyhow::anyhow!("Date overflow when adding months"))?,
        TimeType::Quarter => {
            let m = now.month();
            let m2 = match m {
                1 | 2 | 3 => 3,
                4 | 5 | 6 => 6,
                7 | 8 | 9 => 9,
                10 | 11 | 12 => 12,
                _ => 3,
            };
            let qdate = now
                .with_month(m2)
                .unwrap()
                .with_day(1)
                .unwrap()
                .checked_add_months(Months::new(1))
                .unwrap()
                .checked_sub_days(Days::new(1))
                .unwrap();
            qdate
                .checked_add_months(Months::new(steps * 3))
                .ok_or_else(|| anyhow::anyhow!("Date overflow when adding quarters"))?
        }
        TimeType::Year => now
            .with_month(1)
            .unwrap()
            .with_day(1)
            .unwrap()
            .checked_sub_signed(chrono::Duration::days(1))
            .unwrap()
            .checked_add_months(Months::new(steps * 12))
            .ok_or_else(|| anyhow::anyhow!("Date overflow when adding years"))?,
        TimeType::None => {
            anyhow::bail!("时间类型不能为None");
        }
    };
    Ok(start_time)
}
